{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "LjJ7XrjSK4Kp"
      },
      "source": [
        "##### Copyright 2020 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "cellView": "form",
        "id": "vR6F124oRpTA"
      },
      "outputs": [],
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "ZsYweEXsF1Ut"
      },
      "source": [
        "# Sequential 모델"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "zdxri4XdC3IR"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td><a target=\"_blank\" href=\"https://www.tensorflow.org/guide/keras/sequential_model\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\">TensorFlow.org에서 보기</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs-l10n/blob/master/site/ko/guide/keras/sequential_model.ipynb\" class=\"\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\">Google Colab에서 실행</a></td>\n",
        "  <td><a target=\"_blank\" href=\"https://github.com/tensorflow/docs-l10n/blob/master/site/ko/guide/keras/sequential_model.ipynb\" class=\"\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\">GitHub에서 소스 보기</a></td>\n",
        "  <td><a href=\"https://storage.googleapis.com/tensorflow_docs/docs-l10n/site/ko/guide/keras/sequential_model.ipynb\" class=\"\"><img src=\"https://www.tensorflow.org/images/download_logo_32px.png\">노트북 다운로드</a></td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ygz2642R7AEV"
      },
      "source": [
        "## 설정"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "kPfh5AQ8fFzQ"
      },
      "outputs": [],
      "source": [
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "from tensorflow.keras import layers"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "H86UozfTDwPp"
      },
      "source": [
        "## Sequential 모델을 사용하는 경우\n",
        "\n",
        "`Sequential` 모델은 각 레이어에 **정확히 하나의 입력 텐서와 하나의 출력 텐서**가 있는 **일반 레이어 스택**에 적합합니다.\n",
        "\n",
        "개략적으로 다음과 같은 `Sequential` 모델은"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ajdypoWBxCFi"
      },
      "outputs": [],
      "source": [
        "# Define Sequential model with 3 layers\n",
        "model = keras.Sequential(\n",
        "    [\n",
        "        layers.Dense(2, activation=\"relu\", name=\"layer1\"),\n",
        "        layers.Dense(3, activation=\"relu\", name=\"layer2\"),\n",
        "        layers.Dense(4, name=\"layer3\"),\n",
        "    ]\n",
        ")\n",
        "# Call model on a test input\n",
        "x = tf.ones((3, 3))\n",
        "y = model(x)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "m9gUL80fmFFo"
      },
      "source": [
        "다음 함수와 동일합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "gqksyI6UN0U3"
      },
      "outputs": [],
      "source": [
        "# Create 3 layers\n",
        "layer1 = layers.Dense(2, activation=\"relu\", name=\"layer1\")\n",
        "layer2 = layers.Dense(3, activation=\"relu\", name=\"layer2\")\n",
        "layer3 = layers.Dense(4, name=\"layer3\")\n",
        "\n",
        "# Call layers on a test input\n",
        "x = tf.ones((3, 3))\n",
        "y = layer3(layer2(layer1(x)))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "MqyEgCnQVf4w"
      },
      "source": [
        "Sequential 모델은 다음의 경우에 **적합하지 않습니다**.\n",
        "\n",
        "- 모델에 다중 입력 또는 다중 출력이 있습니다\n",
        "- 레이어에 다중 입력 또는 다중 출력이 있습니다\n",
        "- 레이어 공유를 해야 합니다\n",
        "- 비선형 토폴로지를 원합니다(예: 잔류 연결, 다중 분기 모델)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "sWxFjYzpKzUD"
      },
      "source": [
        "## Sequential 모델 생성하기\n",
        "\n",
        "레이어의 목록을 Sequential 생성자에 전달하여 Sequential 모델을 만들 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "itLg7K6qTzVO"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential(\n",
        "    [\n",
        "        layers.Dense(2, activation=\"relu\"),\n",
        "        layers.Dense(3, activation=\"relu\"),\n",
        "        layers.Dense(4),\n",
        "    ]\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "g3fvbcVmCHEP"
      },
      "source": [
        "속한 레이어는 `layers` 속성을 통해 접근할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Lwmuasfp5KXs"
      },
      "outputs": [],
      "source": [
        "model.layers"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "qVNQRoWqJ3PY"
      },
      "source": [
        "`add()` 메서드를 통해 Sequential 모델을 점진적으로 작성할 수도 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "2TD4yRflSEv6"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential()\n",
        "model.add(layers.Dense(2, activation=\"relu\"))\n",
        "model.add(layers.Dense(3, activation=\"relu\"))\n",
        "model.add(layers.Dense(4))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PmunUrP7HhMI"
      },
      "source": [
        "레이어를 제거하는 `pop()` 메서드도 있습니다. Sequential 모델은 레이어의 리스트와 매우 유사하게 동작합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "6INo3oxSLUbA"
      },
      "outputs": [],
      "source": [
        "model.pop()\n",
        "print(len(model.layers))  # 2"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "AAvcsRrOxQ9W"
      },
      "source": [
        "또한 Sequential 생성자는 Keras의 모든 레이어 또는 모델과 마찬가지로 `name` 인수를 허용합니다. 이것은 의미론적으로 유의미한 이름으로 TensorBoard 그래프에 주석을 달 때 유용합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "s8oN1kMYqSuD"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential(name=\"my_sequential\")\n",
        "model.add(layers.Dense(2, activation=\"relu\", name=\"layer1\"))\n",
        "model.add(layers.Dense(3, activation=\"relu\", name=\"layer2\"))\n",
        "model.add(layers.Dense(4, name=\"layer3\"))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PqGh2rmljliN"
      },
      "source": [
        "## 미리 입력 형상 지정하기\n",
        "\n",
        "일반적으로 Keras의 모든 레이어는 가중치를 만들려면 입력의 형상을 알아야 합니다. 따라서 다음과 같은 레이어를 만들면 처음에는 가중치가 없습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "tW04KX032Ol9"
      },
      "outputs": [],
      "source": [
        "layer = layers.Dense(3)\n",
        "layer.weights  # Empty"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "NU0Ai9lbGUKA"
      },
      "source": [
        "가중치는 모양이 입력의 형상에 따라 달라지기 때문에 입력에서 처음 호출될 때 가중치를 만듭니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "OqSj1TLCU1vH"
      },
      "outputs": [],
      "source": [
        "# Call layer on a test input\n",
        "x = tf.ones((1, 4))\n",
        "y = layer(x)\n",
        "layer.weights  # Now it has weights, of shape (4, 3) and (3,)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CDF0sLoVrGdU"
      },
      "source": [
        "당연히 이것은 Sequential 모델에도 적용됩니다. 입력 형상이 없는 Sequential 모델을 인스턴스화할 때는 \"빌드\"되지 않습니다. 가중치가 없습니다(그리고 `model.weights`를 호출하면 오류가 발생함). 모델에 처음 입력 데이터가 표시되면 가중치가 생성됩니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "ovoPw8Ncbevb"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential(\n",
        "    [\n",
        "        layers.Dense(2, activation=\"relu\"),\n",
        "        layers.Dense(3, activation=\"relu\"),\n",
        "        layers.Dense(4),\n",
        "    ]\n",
        ")  # No weights at this stage!\n",
        "\n",
        "# At this point, you can't do this:\n",
        "# model.weights\n",
        "\n",
        "# You also can't do this:\n",
        "# model.summary()\n",
        "\n",
        "# Call the model on a test input\n",
        "x = tf.ones((1, 4))\n",
        "y = model(x)\n",
        "print(\"Number of weights after calling the model:\", len(model.weights))  # 6"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "laWJparrJg70"
      },
      "source": [
        "모델이 \"빌드\"되면, 그 내용을 표시하기 위해 `summary()` 메서드를 호출할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "mY2X9QbgabHu"
      },
      "outputs": [],
      "source": [
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1ZDoaIHvwSmw"
      },
      "source": [
        "그러나 현재 출력 형상을 포함하여 지금까지 모델의 요약을 표시할 수 있도록 Sequential 모델을 점진적으로 빌드할 때 매우 유용할 수 있습니다. 이 경우 `Input` 객체를 모델에 전달하여 모델의 시작 형상을 알 수 있도록 모델을 시작해야 합니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "Wv9HeHEeCKAG"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential()\n",
        "model.add(keras.Input(shape=(4,)))\n",
        "model.add(layers.Dense(2, activation=\"relu\"))\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "4rhwhV0mpu6m"
      },
      "source": [
        "`Input` 객체는 레이어가 아니므로 `model.layers`의 일부로 표시되지 않습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "oCwEwMoy1B0A"
      },
      "outputs": [],
      "source": [
        "model.layers"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xcKk9VmVY9of"
      },
      "source": [
        "간단한 대안은 첫 번째 레이어에 `input_shape` 인수를 전달하는 것입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "FMe4x1CMP75N"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential()\n",
        "model.add(layers.Dense(2, activation=\"relu\", input_shape=(4,)))\n",
        "\n",
        "model.summary()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "E8lMCSByqIqP"
      },
      "source": [
        "이처럼 사전 정의된 입력 모양으로 빌드된 모델은 항상 가중치를 가지며(데이터를 보기 전에도) 항상 정의된 출력 형상을 갖습니다.\n",
        "\n",
        "일반적으로 Sequential 모델의 입력 형상을 알고 있는 경우 항상 Sequential 모델의 입력 형상을 지정하는 것이 좋습니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "W5tJgMQ6phEp"
      },
      "source": [
        "## 일반적인 디버깅 워크플로우: `add()` + `summary()`\n",
        "\n",
        "새로운 Sequential 아키텍처를 구축할 때는 `add()` 하여 레이어를 점진적으로 쌓고 모델 요약을 자주 인쇄하는 것이 유용합니다. 예를 들어 `Conv2D` 및 `MaxPooling2D` 레이어의 스택이 이미지 특성 맵을 다운 샘플링 하는 방법을 모니터링할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "lz8Lo0OOweir"
      },
      "outputs": [],
      "source": [
        "model = keras.Sequential()\n",
        "model.add(keras.Input(shape=(250, 250, 3)))  # 250x250 RGB images\n",
        "model.add(layers.Conv2D(32, 5, strides=2, activation=\"relu\"))\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.MaxPooling2D(3))\n",
        "\n",
        "# Can you guess what the current output shape is at this point? Probably not.\n",
        "# Let's just print it:\n",
        "model.summary()\n",
        "\n",
        "# The answer was: (40, 40, 32), so we can keep downsampling...\n",
        "\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.MaxPooling2D(3))\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.Conv2D(32, 3, activation=\"relu\"))\n",
        "model.add(layers.MaxPooling2D(2))\n",
        "\n",
        "# And now?\n",
        "model.summary()\n",
        "\n",
        "# Now that we have 4x4 feature maps, time to apply global max pooling.\n",
        "model.add(layers.GlobalMaxPooling2D())\n",
        "\n",
        "# Finally, we add a classification layer.\n",
        "model.add(layers.Dense(10))"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CNbDnMrNPCWu"
      },
      "source": [
        "매우 실용적이죠?\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "doMLAEStLF8l"
      },
      "source": [
        "## 모델이 완성되면 해야 할 일\n",
        "\n",
        "모델 아키텍처가 준비되면 다음을 수행할 수 있습니다.\n",
        "\n",
        "- 모델을 훈련시키고 평가하며 추론을 실행합니다. [내장 루프를 사용한 훈련 및 평가 가이드](https://www.tensorflow.org/guide/keras/train_and_evaluate/)를 참조하세요.\n",
        "- 모델을 디스크에 저장하고 복구합니다. [직렬화 및 저장 가이드](https://www.tensorflow.org/guide/keras/save_and_serialize/)를 참조하세요.\n",
        "- 다중 GPU를 활용하여 모델의 훈련 속도를 향상합니다. [다중 GPU 및 분산 훈련 가이드](distributed_training)를 참조하세요."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "tQ1VzU9LjW6O"
      },
      "source": [
        "## Sequential 모델을 사용한 특성 추출\n",
        "\n",
        "Sequential 모델이 빌드되면 [Functional API 모델](https://www.tensorflow.org/guide/keras/functional/)처럼 동작합니다. 이는 모든 레이어가 `input` 및 `output` 속성을 갖는다는 것을 의미합니다. 이러한 속성을 사용하면 Sequential 모델 내의 모든 중간 레이어들의 출력을 추출하는 모델을 빠르게 생성하는 등 깔끔한 작업을 수행할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "DCnNkNL4EEWT"
      },
      "outputs": [],
      "source": [
        "initial_model = keras.Sequential(\n",
        "    [\n",
        "        keras.Input(shape=(250, 250, 3)),\n",
        "        layers.Conv2D(32, 5, strides=2, activation=\"relu\"),\n",
        "        layers.Conv2D(32, 3, activation=\"relu\"),\n",
        "        layers.Conv2D(32, 3, activation=\"relu\"),\n",
        "    ]\n",
        ")\n",
        "feature_extractor = keras.Model(\n",
        "    inputs=initial_model.inputs,\n",
        "    outputs=[layer.output for layer in initial_model.layers],\n",
        ")\n",
        "\n",
        "# Call feature extractor on test input.\n",
        "x = tf.ones((1, 250, 250, 3))\n",
        "features = feature_extractor(x)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "oX0vtFJEr8mC"
      },
      "source": [
        "다음은 한 레이어에서 특성만 추출하는 것과 유사한 예입니다."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "id": "4fCuSyOQFkbq"
      },
      "outputs": [],
      "source": [
        "initial_model = keras.Sequential(\n",
        "    [\n",
        "        keras.Input(shape=(250, 250, 3)),\n",
        "        layers.Conv2D(32, 5, strides=2, activation=\"relu\"),\n",
        "        layers.Conv2D(32, 3, activation=\"relu\", name=\"my_intermediate_layer\"),\n",
        "        layers.Conv2D(32, 3, activation=\"relu\"),\n",
        "    ]\n",
        ")\n",
        "feature_extractor = keras.Model(\n",
        "    inputs=initial_model.inputs,\n",
        "    outputs=initial_model.get_layer(name=\"my_intermediate_layer\").output,\n",
        ")\n",
        "# Call feature extractor on test input.\n",
        "x = tf.ones((1, 250, 250, 3))\n",
        "features = feature_extractor(x)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "CdWf9KFXXjM4"
      },
      "source": [
        "## Sequential 모델을 통한 전이 학습\n",
        "\n",
        "전이 학습은 모델에서 맨 아래 레이어를 동결하고 맨 위 레이어만 훈련하는 것으로 구성됩니다. 익숙하지 않은 경우, [전이 학습 가이드](https://www.tensorflow.org/guide/keras/transfer_learning/)를 읽어보세요.\n",
        "\n",
        "다음은 Sequential 모델과 관련된 두 가지 일반적인 전이 학습 청사진입니다.\n",
        "\n",
        "먼저 Sequential 모델이 있고 마지막 모델을 제외한 모든 레이어를 동결하려고 한다고 가정합니다. 이 경우 다음과 같이 단순히 `model.layers`를 반복하고 마지막 레이어를 제외하고 각 레이어에서 `layer.trainable = False`를 설정합니다.\n",
        "\n",
        "```python\n",
        "model = keras.Sequential([<br>    keras.Input(shape=(784))<br>    layers.Dense(32, activation='relu'),<br>    layers.Dense(32, activation='relu'),<br>    layers.Dense(32, activation='relu'),<br>    layers.Dense(10),<br>])<br> <br># Presumably you would want to first load pre-trained weights.<br>model.load_weights(...)<br> <br># Freeze all layers except the last one.<br>for layer in model.layers[:-1]:<br>  layer.trainable = False<br> <br># Recompile and train (this will only update the weights of the last layer).<br>model.compile(...)<br>model.fit(...)\n",
        "```\n",
        "\n",
        "또 다른 일반적인 청사진은 다음과 같이 Sequential 모델을 사용하여 사전 훈련된 모델과 새로 초기화된 분류 레이어를 쌓는 것입니다.\n",
        "\n",
        "```python\n",
        "# Load a convolutional base with pre-trained weights<br>base_model = keras.applications.Xception(<br>    weights='imagenet',<br>    include_top=False,<br>    pooling='avg')<br> <br># Freeze the base model<br>base_model.trainable = False<br> <br># Use a Sequential model to add a trainable classifier on top<br>model = keras.Sequential([<br>    base_model,<br>    layers.Dense(1000),<br>])<br> <br># Compile & train<br>model.compile(...)<br>model.fit(...)\n",
        "```\n",
        "\n",
        "전이 학습을 한다면 아마도 이 두 가지 패턴을 자주 사용하게 될 것입니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "weB4ei6uNtIg"
      },
      "source": [
        "이것이 Sequential 모델에 대해 알아야 할 전부입니다!\n",
        "\n",
        "Keras에서 모델을 빌드하는 방법에 대한 자세한 내용은 다음을 참조하세요.\n",
        "\n",
        "- [Functional API 가이드](https://www.tensorflow.org/guide/keras/functional/)\n",
        "- [하위 클래스화를 통한 새 레이어 및 모델 생성 가이드](https://www.tensorflow.org/guide/keras/custom_layers_and_models/)"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "collapsed_sections": [],
      "name": "sequential_model.ipynb",
      "toc_visible": true
    },
    "kernelspec": {
      "display_name": "Python 3",
      "name": "python3"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
